#include "tractasklistcontroller.h"
#include "qdebug.h"

#include "ticketexplorer.h"
#include "tracticketexplorerbackend.h"
#include "ticketquery.h"
#include "ticketitemmodel.h"
#include "singleticketwidget.h"
#include "ticketssidebar.h" // @@@ this needs to be the search widget soon
#include "tasklistmodelutil.h"

#include <QxtCore/QxtLogger>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/project.h>
#include "traccreatequerydialog.h"


using namespace ProjectExplorer;

class TaskListControllerPrivate
{
public:
    TaskListControllerPrivate()
        : m_explorer(0) //new TicketExplorer(new TracTicketExplorerBackend(0, 0)))
    {
    }

    ~TaskListControllerPrivate()
    {
        delete m_explorer;
    }

    void setConfiguration(ITicketBackendConfiguration *conf)
    {
        if (m_explorer) {
            m_explorer->disconnect();
            delete m_explorer;
            m_explorer = 0;
        }
        ITicketExplorerBackend *backend = 0;

        // TODO: This should be done dynamically
        if (conf->id() == "trac") {
            backend = new TracTicketExplorerBackend(conf);
        }

        if (backend)
        {
            m_explorer = new TicketExplorer(backend);
        }
    }

    inline TicketExplorer *explorer() const { return m_explorer; }

private:
    TicketExplorer *m_explorer;
};

TracTaskListController::TracTaskListController():
    m_project(0),
    d(new TaskListControllerPrivate),
    queryDialog(0),
    progressObject(0),
    initialQueriesCreated(false),
    m_settings("Nokia", "Tickets Plugin"),
    m_currentRoot(0),
    m_configurationReset(false)
{
    m_proxy = new QSortFilterProxyModel(this);
    m_proxy->setDynamicSortFilter(true);
    connect(ProjectExplorerPlugin::instance(), SIGNAL(currentProjectChanged(ProjectExplorer::Project *)),
            this, SLOT(projectChanged(ProjectExplorer::Project *)));
}

TracTaskListController::~TracTaskListController()
{
    delete d;
}

void TracTaskListController::root()
{
    if (d && d->explorer()) {
        connect(d->explorer(), SIGNAL(error(TicketError)), this, SLOT(onError(TicketError)));
        connect(d->explorer(), SIGNAL(fieldDefinitionsUpdated()), this, SLOT(fieldsUpdated()));
        createInitialQuery();
        restoreSettings();
    }
}

void TracTaskListController::container(QString identifier)
{
    Q_UNUSED(identifier);
}

void TracTaskListController::syncVersion()
{
}
void TracTaskListController::syncContainer(QString identifier)
{
    Q_UNUSED(identifier);
}
void TracTaskListController::getImage(QString identifier)
{
    Q_UNUSED(identifier);
}
void TracTaskListController::openTask(TaskHandle *task)
{
    if (!d->explorer())
        return;

    if (!m_proxy->sourceModel()) return;
    BaseTicketItemModel *model = (BaseTicketItemModel*) m_proxy->sourceModel();

    Ticket *ticket = 0;
    for (int i=0;i<model->rowCount(QModelIndex());i++)
    {
        if (QString::number(model->ticketAtIndex(model->index(i))->id()) == task->taskId())
        {
            ticket = model->ticketAtIndex(model->index(i));
        }
    }

    if (!ticket)
    {
        //look in map
        QMapIterator<QString, BaseTicketItemModel*> it (m_models);
        while (it.hasNext() && !ticket)
        {
            it.next();
            BaseTicketItemModel *old_model = it.value();
            for (int i=0;i<old_model->rowCount(QModelIndex());i++)
            {
                if (QString::number(old_model->ticketAtIndex(old_model->index(i))->id()) == task->taskId())
                {
                    ticket = old_model->ticketAtIndex(old_model->index(i));
                }
            }
        }
    }
    if (ticket) {
        QPointer<SingleTicketWidget> m_ticketWindow = new SingleTicketWidget(d->explorer());
        m_ticketWindow->setWindowModality(Qt::WindowModal);
        m_ticketWindow->setWindowFlags(Qt::Window);
        m_ticketWindow->setFormMode(SingleTicketWidget::EditTicket);
        m_ticketWindow->loadTicket(ticket);
        m_ticketWindow->show();
    }
}
void TracTaskListController::openTaskById(QString taskId, QString repoUrl)
{
    Q_UNUSED(taskId);
    Q_UNUSED(repoUrl);
}
void TracTaskListController::newTask(bool isLocal)
{
    Q_UNUSED(isLocal);
    if (!d || !d->explorer() || !m_project)
        return;
    SingleTicketWidget *sw = new SingleTicketWidget(d->explorer());
    sw->setWindowModality(Qt::WindowModal);
    sw->setWindowFlags(Qt::Window);
    sw->setFormMode(SingleTicketWidget::NewTicket);
    sw->show();
}
void TracTaskListController::syncRoot()
{
    if (d && d->explorer()) {
        connect(d->explorer(), SIGNAL(error(TicketError)), this, SLOT(onError(TicketError)));
        connect(d->explorer(), SIGNAL(fieldDefinitionsUpdated()), this, SLOT(fieldsUpdated()));
        createInitialQuery(true);
        restoreSettings();
    }
}
void TracTaskListController::syncTask(QString identifier)
{
    Q_UNUSED(identifier);
}
void TracTaskListController::markTaskRead(TaskHandle *task)
{
    Q_UNUSED(task);
}
void TracTaskListController::markTaskUnread(TaskHandle *task)
{
    Q_UNUSED(task);
}
void TracTaskListController::deleteTask(TaskHandle *task)
{
    Q_UNUSED(task);
}
void TracTaskListController::markContainerRead(TaskContainer *container)
{
    Q_UNUSED(container);
}
void TracTaskListController::deleteContainer(TaskContainer *container)
{
    if (m_currentRoot && m_currentContainers.contains(container))
    {
        m_currentContainers.removeOne(container);
        QListIterator<TaskContainerHandle*> it (m_currentRoot->containers());
        TaskContainerHandle *handle = 0;
        while (it.hasNext() && !handle)
        {
            TaskContainerHandle *h = it.next();
            if (h->identifier() == container->identifier())
            {
                handle = h;
            }
        }
        if (handle)
        {
            QList<TaskContainerHandle*> containers = m_currentRoot->containers();
            containers.removeOne(handle);
            m_currentRoot->setContainers(containers);
        }

        //delete query from settings
        if (m_project && !m_project->displayName().isEmpty())
        {
            m_settings.beginGroup(m_project->displayName());
            if (m_settings.childGroups().contains(container->identifier().toUpper()))
            {
                m_settings.beginGroup(container->identifier().toUpper());
                m_settings.remove("");
                m_settings.endGroup();
            }
            m_settings.endGroup();
        }

        refreshQueriesFromModel(0,"");
    }
}
bool TracTaskListController::shutdownTasktopIfInvisible()
{
    return true;  // @@@
}
void TracTaskListController::onCallFinished()
{
}
void TracTaskListController::newRepository()
{
}
void TracTaskListController::editRepository(QString repositoryUrl)
{
    Q_UNUSED(repositoryUrl);
}
void TracTaskListController::updateRepositoryConfiguration(QString containerHandle)
{
    Q_UNUSED(containerHandle);
}
void TracTaskListController::newQuery(QString repositoryUrl)
{
    Q_UNUSED(repositoryUrl); //url taken from QSettings
    if (!d || !d->explorer() || !m_project) return;
    if (!queryDialog)
    {
        queryDialog = new TracCreateQueryDialog(m_types);
        queryDialog->setModal(true);
        queryDialog->setWindowTitle(QString("Trac Query"));
        queryDialog->show();
        QObject::connect(queryDialog, SIGNAL(finished(int)), this, SLOT(createNewQuery(int)));
    }
}
void TracTaskListController::createNewQuery(int result)
{
    if (queryDialog)
    {
        QMap<QString, QString> queryPieces = queryDialog->getQueryPieces();
        queryDialog->deleteLater();
        queryDialog = 0;
        if (result == QDialog::Accepted)
        {
            //create query
            TicketQuery query("", TicketQuery::Advanced);
            query.setLabel(queryPieces["label"]);
            query.addLookupField("description",
                                 TicketQuery::OperatorContains, queryPieces["description"]);
            query.addLookupField("summary", TicketQuery::OperatorContains,queryPieces["summary"]);
            query.addLookupField("owner", TicketQuery::OperatorIs, queryPieces["owner"]);
            if (queryPieces["type"] != "<any>")
            {
                query.addLookupField("type", TicketQuery::OperatorIs, queryPieces["type"]);
            }
            bool showClosed = queryPieces["includeClosed"] == "TRUE";

            foreach (QString status, m_statuses) {
                bool include = true;
                if (!showClosed && status == "closed")
                    include = false;
                query.includeStatus(status, include);
            }

            //create and invoke query
            if (d->explorer())
                setTicketModel(d->explorer()->modelQuery(query), query);

            //store query
            if (m_project && !m_project->displayName().isEmpty())
            {
                m_settings.beginGroup(m_project->displayName());
                m_settings.beginGroup(query.label().toUpper());
                m_settings.setValue("Query_label", query.label());
                m_settings.setValue("Query_summary", queryPieces["summary"]);
                m_settings.setValue("Query_description", queryPieces["description"]);
                m_settings.setValue("Query_owner", queryPieces["owner"]);
                m_settings.setValue("Query_type", queryPieces["type"]);
                m_settings.setValue("Query_closed", queryPieces["includeClosed"]);
                m_settings.endGroup();
                m_settings.endGroup();
            }
        }
    }
}
void TracTaskListController::editQuery(QString containerHandle)
{
    Q_UNUSED(containerHandle);
}
void TracTaskListController::initEventHandler()
{
}
void TracTaskListController::activeTask()
{
}
void TracTaskListController::deactivateTasks()
{
}
void TracTaskListController::activateTask(QString identifier)
{
    Q_UNUSED(identifier);
}
void TracTaskListController::newSupportRequest()
{
}
void TracTaskListController::cloneQuery(QString identifier)
{
    Q_UNUSED(identifier);
}
void TracTaskListController::openTimeTrackingDashboard()
{
}
void TracTaskListController::importTaskList()
{
}
void TracTaskListController::exportTaskList()
{
}

void TracTaskListController::projectChanged(ProjectExplorer::Project *project)
{
    if (!project) {
        //No project selected
        return;
    }
    m_project = project;
    ITicketBackendConfiguration *conf = 0;
    QSettings settings("Nokia", "Tickets Plugin");
    settings.beginGroup(project->displayName());
    if (settings.value("ID").toString() == "trac") {
        TracTicketConfiguration *tc = new TracTicketConfiguration();
        tc->readSettings(settings);

        if (tc->url().isEmpty()) {
            delete tc;
        } else {
            conf = tc;
            m_currentBackendUrl = tc->url();
        }
    }
    settings.endGroup();

    if (conf) {
        changeConfiguration(conf);
    }
}

void TracTaskListController::changeConfiguration(ITicketBackendConfiguration *conf)
{
    d->setConfiguration(conf);

    if (d->explorer()) {
        connect(d->explorer(), SIGNAL(error(TicketError)), this, SLOT(onError(TicketError)));
        connect(d->explorer(), SIGNAL(fieldDefinitionsUpdated()), this, SLOT(fieldsUpdated()));
        m_types.clear();
        m_statuses.clear();
        m_configurationReset = true;
        d->explorer()->updateFieldDefinitions();
        m_currentRoot = 0;
        m_currentContainers.clear();
        //dispose old models
        QMapIterator<QString, BaseTicketItemModel*> it (m_models);
        while (it.hasNext())
        {
            it.next();
            delete it.value();
        }
        m_models.clear();
        restoreSettings();
    }
}

void TracTaskListController::fieldsUpdated()
{
    if (!d->explorer())
        return;

    QList<TicketField> fields = d->explorer()->getAvailableFields();
    foreach (TicketField field, fields) {
        if (field.name == "type")
            m_types = field.options;
        else if (field.name == "status")
            m_statuses = field.options;
    }
    if (m_configurationReset)
    {
        m_configurationReset = false;
        createInitialQuery(true);
    }
}

void TracTaskListController::onError(const TicketError &error)
{
    Error *tasklistError = new Error();
    tasklistError->setMessage(error.error());
    tasklistError->setCode(error.type());
    if (error.ticket())
        tasklistError->setDetail(QString::number(error.ticket()->id()).prepend("Ticket: "));
    emit onError(tasklistError);
}

void TracTaskListController::setTicketModel(BaseTicketItemModel *model, TicketQuery query)
{
    //don't delete old model just yet. still need it to open tickets of the old model. will be deleted on changeCOnfiguration
    //QAbstractItemModel *oldModel = m_proxy->sourceModel();
    /*if (oldModel && model != oldModel)
        delete oldModel;
    */
    model->setParent(this);
    model->setTicketQuery(query);
    m_proxy->setSourceModel(model);
    currentContainerIdentifier = "";
    if (progressObject)
    {
        //end old progress
        progressObject->reportFinished();
        delete progressObject;
        progressObject = 0;
    }

    m_models.insert(query.label(), model);
    QObject::connect(model, SIGNAL(queryCompleted(int,int)), this, SLOT(queryComplete(int,int)));
}

void TracTaskListController::queryComplete(int done, int total)
{
    if (currentContainerIdentifier.isEmpty()) {
        currentContainerIdentifier = QTime::currentTime().toString();
    }
    if (!m_proxy->sourceModel()) return;
    BaseTicketItemModel *model = (BaseTicketItemModel*) m_proxy->sourceModel();

    TicketQuery query = model->query();
    QString queryTitle = query.label();

    //this ensures that we only do two updates: one to show the (empty) new query, the other to show the query with all results
    //that way we prevent too much copying and UI updates (flickering)
    if (done <= 1 || total <= done)
    {
        refreshQueriesFromModel(model, queryTitle);
    }

    //if auto Query, init new query
    if (total <= done && initialQueriesCreated && !lastAutoCreatedQuery.isEmpty()) {
        autoQueryDone(queryTitle);
    }

    //progress
    if (!progressObject)
    {
        progressObject = new QFutureInterface<void>;
        progressObject->setProgressRange(0,total);
        Core::ICore::instance()->progressManager()->addTask(progressObject->future(), "Querying", "Tasktop", Core::ProgressManager::ShowInApplicationIcon);
        progressObject->reportStarted();
    }
    if (progressObject)
    {
        progressObject->setProgressValue(done);
    }
    if (done >= total && progressObject)
    {
        progressObject->reportFinished();
        delete progressObject;
        progressObject = 0;
    }
}

void TracTaskListController::refreshQueriesFromModel(BaseTicketItemModel *model, QString queryTitle)
{
    //emit root and container update
    TaskListRoot *root = TasklistModelUtil::rootFromPreviousRoot(m_currentRoot, m_currentBackendUrl.toString(QUrl::RemoveUserInfo), queryTitle);
    QList<TaskContainer*> containers;
    containers = TasklistModelUtil::containersFromPreviousContainers(m_currentContainers, model, m_currentBackendUrl.toString(QUrl::RemoveUserInfo), queryTitle);
    m_currentRoot = root;
    m_currentContainers = containers;
    emit onModelChanged(root);
    QListIterator<TaskContainer*> it(containers);
    while (it.hasNext())
    {
        TaskContainer *container = it.next();
        emit onContainerChanged(container);
    }
}

void TracTaskListController::restoreSettings()
{
    Core::ICore::instance()->settings();
}

void TracTaskListController::createInitialQuery(bool forceReset)
{
    if (forceReset)
    {
        initialQueriesCreated = false;
        lastAutoCreatedQuery = "";
        m_currentRoot = 0;
        m_currentContainers.clear();
        //dispose old models
        QMapIterator<QString, BaseTicketItemModel*> it (m_models);
        while (it.hasNext())
        {
            it.next();
            delete it.value();
        }
        m_models.clear();
    }
    if (!initialQueriesCreated)
    {
        initialQueriesCreated = true;
        //check whether we have a stored query
        if (m_project && !m_project->displayName().isEmpty() && m_settings.childGroups().contains(m_project->displayName()))
        {
            m_settings.beginGroup(m_project->displayName());
            if (m_settings.childGroups().size() > 0)
            {
                m_settings.beginGroup(m_settings.childGroups().first());
                //found stored queries.
                //use first one
                if (!m_settings.value("Query_label", "").toString().isEmpty())
                {
                    TicketQuery query = queryFromSettings();

                    lastAutoCreatedQuery = query.label();
                    //submit query
                    setTicketModel(d->explorer()->modelQuery(query), query);
                    m_settings.endGroup();
                    m_settings.endGroup();
                    return;
                }
                m_settings.endGroup();
            }
            m_settings.endGroup();
        }
        //no stored query found, create AllTickets query
        setTicketModel(d->explorer()->modelQuery(TicketQuery()));
    }
}

TicketQuery TracTaskListController::queryFromSettings()
{
    TicketQuery query("", TicketQuery::Advanced);
    query.setLabel(m_settings.value("Query_label", "").toString());
    query.addLookupField("description", TicketQuery::OperatorContains, m_settings.value("Query_description", "").toString());
    query.addLookupField("summary", TicketQuery::OperatorContains, m_settings.value("Query_summary", "").toString());
    query.addLookupField("owner", TicketQuery::OperatorIs, m_settings.value("Query_owner", "").toString());
    if (m_settings.value("Query_type", "").toString() != "<any>")
        query.addLookupField("type", TicketQuery::OperatorIs, m_settings.value("Query_type", "").toString());
    bool showClosed = (m_settings.value("Query_closed", "").toString()) == "TRUE";

    foreach (QString status, m_statuses) {
        bool include = true;
        if (!showClosed && status == "closed")
            include = false;
        query.includeStatus(status, include);
    }
    return query;
}

void TracTaskListController::autoQueryDone(QString queryLabel)
{
    if (queryLabel.isEmpty())
        return;
    m_settings.beginGroup(m_project->displayName());
    if (m_settings.childGroups().size() > 0)
    {
        //find the query after "queryLabel" in the settings
        QString nextQuery;
        QListIterator<QString> it (m_settings.childGroups());
        while (it.hasNext() && nextQuery.isEmpty())
        {
            if (it.next().toLower() == queryLabel.toLower())
            {
                if (it.hasNext())
                {
                    nextQuery = it.next();
                }
            }
        }
        if (nextQuery.isEmpty())
        {
            //appears there is no more query, so we are done here
            lastAutoCreatedQuery = "";
        }
        else
        {
            //found a next query, go ahead
            m_settings.beginGroup(nextQuery);
            //found stored queries.
            //use first one
            if (!m_settings.value("Query_label", "").toString().isEmpty())
            {
                TicketQuery query = queryFromSettings();
                lastAutoCreatedQuery = query.label();
                //submit query
                setTicketModel(d->explorer()->modelQuery(query), query);
            }
            m_settings.endGroup();
        }
    }
    m_settings.endGroup();
}
